package top.continew.admin.generator.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import top.continew.admin.common.config.properties.WxMaProperties;
import top.continew.admin.common.context.UserContext;
import top.continew.admin.common.context.UserContextHolder;
import top.continew.admin.common.enums.NotifyStatusEnums;
import top.continew.admin.common.enums.TaskStatusEnums;
import top.continew.admin.generator.mapper.TaskListMapper;
import top.continew.admin.generator.model.entity.TaskAssignmentDO;
import top.continew.admin.generator.model.entity.TaskListDO;
import top.continew.admin.generator.model.query.TaskListCustomQuery;
import top.continew.admin.generator.model.query.TaskListQuery;
import top.continew.admin.generator.model.req.TaskListReq;
import top.continew.admin.generator.model.req.TransferApplyReq;
import top.continew.admin.generator.model.req.TransferHandleReq;
import top.continew.admin.generator.model.resp.TaskListDetailResp;
import top.continew.admin.generator.model.resp.TaskListResp;
import top.continew.admin.generator.service.*;
import top.continew.admin.open.model.req.WxSubscribeMessageReq;
import top.continew.admin.open.service.NotifyService;
import top.continew.admin.system.model.entity.DictItemDO;
import top.continew.admin.system.model.entity.user.UserDO;

import java.util.Collections;

import top.continew.admin.system.model.req.TaskHandlerReq;
import top.continew.admin.system.model.resp.MyTaskExportRes;
import top.continew.admin.system.model.resp.MyTaskRes;
import top.continew.admin.system.model.resp.MyTaskStatisticsRes;
import top.continew.admin.system.model.resp.TaskAssignmentHandlerResp;
import top.continew.admin.system.model.resp.user.UserDetailResp;
import top.continew.admin.system.model.resp.ExportTaskResp;
import top.continew.admin.system.service.DictItemService;
import top.continew.admin.system.service.UserService;
import top.continew.admin.system.service.ExportTaskService;
import top.continew.admin.system.model.entity.ExportTaskDO;
import top.continew.admin.common.service.CloudStorageService;
import org.springframework.scheduling.annotation.Async;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.extension.crud.model.resp.PageResp;
import top.continew.starter.extension.crud.service.BaseServiceImpl;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.Set;

/**
 * 任务管理业务实现
 *
 * @author jiangjun
 * @since 2025/06/20 17:28
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TaskListServiceImpl extends BaseServiceImpl<TaskListMapper, TaskListDO, TaskListResp, TaskListDetailResp, TaskListQuery, TaskListReq> implements TaskListService {

    private final DictItemService dictItemService;
    private final TaskAssignmentService taskAssignmentService;
    private final UserService userService;
    private final NotifyService notifyService;
    private final WxMaProperties wxMaProperties;
    private final ImportBatchService importBatchService;
    private final TaskTransferService taskTransferService;
    private final SmsService smsService;
    private final ExportTaskService exportTaskService;
    private final CloudStorageService cloudStorageService;


    @Override
    public String importExcel(MultipartFile file) {
        String batchId = null;
        int failCount = 0;
        int successCount = 0;
        try {
            // 创建导入批次（先预估总数为0，后续更新）
            batchId = importBatchService.createBatch(file.getOriginalFilename(), 0);

            // 获取省区字典数据
            List<DictItemDO> provinceDict = dictItemService.list(
                    new LambdaQueryWrapper<DictItemDO>()
                            .eq(DictItemDO::getDictId, "726487407381192811")
            );

            // 先清空一次缓存
            taskAssignmentService.cleanProvinceHandlersCache();

            // 使用流式读取处理大文件
            ExcelImportListener listener = new ExcelImportListener(batchId, provinceDict);
            EasyExcel.read(file.getInputStream())
                    .sheet()
                    .headRowNumber(1)
                    .registerReadListener(listener)
                    .doRead();

            // 处理剩余数据
            listener.processRemaining();

            successCount = listener.getSuccessCount();
            failCount = listener.getFailCount();
            // 更新批次状态
            String resultMsg = String.format("导入完成：成功%d条，失败%d条", successCount, failCount);
            importBatchService.updateBatchStatus(batchId, "completed", listener.getSuccessCount(), listener.getFailCount(), resultMsg, successCount + failCount);

            // 推送微信消息给有任务分配的负责人
            pushWxMsgToAssignedHandlers();
            return resultMsg;

        } catch (IOException e) {
            log.error("读取Excel文件失败", e);
            if (batchId != null) {
                importBatchService.updateBatchStatus(batchId, "failed", successCount, failCount, "文件读取错误", null);
            }
            return "导入失败：文件读取错误";
        } catch (Exception e) {
            log.error("导入Excel失败", e);
            if (batchId != null) {
                importBatchService.updateBatchStatus(batchId, "failed", successCount, failCount, "导入Excel失败", null);
            }
            return "导入失败：" + e.getMessage();
        }
    }

    /**
     * 匹配省区编码
     */
    private String matchProvinceCode(String provinceName, List<DictItemDO> provinceDict) {
        if (StrUtil.isBlank(provinceName) || "无".equals(provinceName)) {
            return null;
        }

        // 精确匹配
        for (DictItemDO item : provinceDict) {
            if (provinceName.equals(item.getLabel())) {
                return item.getValue();
            }
        }

        // 模糊匹配（去掉省市后缀）
        String cleanProvinceName = provinceName.replace("省", "").replace("市", "");
        for (DictItemDO item : provinceDict) {
            String cleanLabel = item.getLabel().replace("省", "").replace("市", "");
            if (cleanProvinceName.equals(cleanLabel) ||
                    cleanProvinceName.contains(cleanLabel) ||
                    cleanLabel.contains(cleanProvinceName)) {
                return item.getValue();
            }
        }

        return null;
    }

    @Override
    public Object getPriceAnalysis(String categoryName, String spec, String provinceName, String startDate, String endDate) {
        List<Map<String, Object>> priceData = baseMapper.selectPriceAnalysis(categoryName, spec, provinceName, startDate, endDate);
        return priceData != null ? priceData : new ArrayList<>();
    }

    @Override
    public Object getWorkloadStats(String provinceName, String handler, String startDate, String endDate) {
        Map<String, Object> result = new HashMap<>();

        // 获取任务总数统计
        Map<String, Long> taskCounts = getTaskCounts(provinceName, handler, startDate, endDate);
        result.putAll(taskCounts);

        // 获取省区统计
        List<Map<String, Object>> provinceRanking = getProvinceRanking(provinceName, handler, startDate, endDate);
        result.put("provinceRanking", provinceRanking);

        // 获取负责人排行榜
        List<Map<String, Object>> handlerRanking = getHandlerRanking(provinceName, handler, startDate, endDate);
        result.put("handlerRanking", handlerRanking);

        // 获取产品维度统计
        List<Map<String, Object>> categoryStats = getCategoryRanking(provinceName, handler, startDate, endDate);
        result.put("categoryStats", categoryStats);

        return result;
    }


    @Override
    public PageResp<MyTaskRes> queryByType(String type, Integer pageSize, Integer pageNum, String shopName, String categoryName) {
        Long userId = UserContextHolder.getUserId();
        UserDetailResp userDetailResp = userService.get(userId);
        if (null != userDetailResp && StringUtils.isNotEmpty(userDetailResp.getPhone())) {
            IPage<MyTaskRes> page = this.baseMapper.selectTaskPage(new Page<>(pageNum, pageSize), type, userId, shopName, categoryName);
            return PageResp.build(page);
        }
        return PageResp.build(new Page<>(pageNum, pageSize, 0));
    }

    @Override
    public PageResp<MyTaskRes> myPageQuery(TaskListCustomQuery c) {
        IPage<MyTaskRes> page = this.baseMapper.myPageQuery(new Page<>(c.getPage(), c.getSize()), c);
        if (CollectionUtils.isNotEmpty(page.getRecords())) {
            // 查询负责人信息
            List<TaskAssignmentHandlerResp> assignmentHandlers = taskAssignmentService.selectHandlers(page.getRecords()
                    .stream().map(MyTaskRes::getId).toList());
            for (MyTaskRes record : page.getRecords()) {
                if (StringUtils.isNotEmpty(record.getHandlePicUrl())) {
                    record.setImages(Arrays.stream(record.getHandlePicUrl().split(",")).toList());
                }
                record.setDirectors(assignmentHandlers.stream().filter(ah -> ah.getTaskId().equals(record.getId()))
                        .map(TaskAssignmentHandlerResp::getHandlerName).collect(Collectors.joining()));
                if (StringUtils.isNotEmpty(record.getHandler())) {
                    record.setHandlerName(assignmentHandlers.stream().map(TaskAssignmentHandlerResp::getHandlerName)
                            .filter(handlerName -> handlerName.equals(record.getHandler())).collect(Collectors.joining()));
                }
            }
        }
        return PageResp.build(page);
    }

    @Override
    public void myExport(TaskListCustomQuery query, HttpServletResponse response) {
        try {
            List<MyTaskRes> exportList = this.baseMapper.myPageQuery(new Page<>(1, Integer.MAX_VALUE), query).getRecords();
            List<MyTaskExportRes> list = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(exportList)) {

                // 查询负责人信息
                List<TaskAssignmentHandlerResp> assignmentHandlers = taskAssignmentService.selectHandlers(exportList.stream().map(MyTaskRes::getId).toList());

                // 获取转派链条和附件信息
                Map<Long, List<Map<String, Object>>> transferChainMap = baseMapper.selectTransferChainForExport(exportList.stream().map(MyTaskRes::getId).collect(Collectors.toSet()))
                        .stream().collect(Collectors.groupingBy(t -> Long.valueOf(t.get("taskId").toString())));

                for (MyTaskRes myTaskRes : exportList) {
                    MyTaskExportRes exportRes = new MyTaskExportRes();
                    BeanUtils.copyProperties(myTaskRes, exportRes);
                    exportRes.setTaskStatus(TaskStatusEnums.getByValue(myTaskRes.getTaskStatus()).orElse(TaskStatusEnums.UN_DO).getDescription());
                    exportRes.setWxMsgNotice(NotifyStatusEnums.getByValue(myTaskRes.getWxMsgNotice()).orElse(NotifyStatusEnums.UN_DO).getDescription());
                    exportRes.setVoiceNotice(NotifyStatusEnums.getByValue(myTaskRes.getVoiceNotice()).orElse(NotifyStatusEnums.UN_DO).getDescription());
                    if (null != myTaskRes.getCompleteTime()) {
                        exportRes.setCompleteTimeStr(DateUtil.format(myTaskRes.getCompleteTime(), "yyyy-MM-dd"));
                    }
                    exportRes.setCreateTimeStr(DateUtil.format(myTaskRes.getCreateTime(), "yyyy-MM-dd"));
                    exportRes.setDirectors(assignmentHandlers.stream().filter(ah -> ah.getTaskId().equals(myTaskRes.getId()))
                            .map(TaskAssignmentHandlerResp::getHandlerName).collect(Collectors.joining()));
                    if (StringUtils.isNotEmpty(myTaskRes.getHandler())) {
                        exportRes.setHandlerName(assignmentHandlers.stream().map(TaskAssignmentHandlerResp::getHandlerName).distinct()
                                .filter(handlerName -> handlerName.equals(myTaskRes.getHandler())).collect(Collectors.joining(",")));
                    }

                    // 获取当前任务的转派链条
                    List<Map<String, Object>> taskTransferChain = transferChainMap.get(myTaskRes.getId());
                    if (CollectionUtils.isNotEmpty(taskTransferChain)) {
                        String chainStr = taskTransferChain.stream()
                                .map(t -> {
                                    String s = t.get("fromUserName") + "→";
                                    if (null != t.get("toUserName")) {
                                        s += t.get("toUserName");
                                    }
                                    return s;
                                })
                                .collect(Collectors.joining("→"));
                        exportRes.setTransferChain(chainStr);

                        String attachments = taskTransferChain.stream()
                                .map(t -> (String) t.get("attachmentUrls"))
                                .filter(StrUtil::isNotBlank)
                                .collect(Collectors.joining(";"));
                        exportRes.setTransferAttachments(attachments);
                    } else {
                        exportRes.setTransferChain("");
                        exportRes.setTransferAttachments("");
                    }

                    list.add(exportRes);
                }
            }

            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("任务管理导出_" + DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss"), StandardCharsets.UTF_8).replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=" + fileName + ".xlsx");

            EasyExcel.write(response.getOutputStream(), MyTaskExportRes.class)
                    .sheet("任务管理")
                    .doWrite(list);
        } catch (Exception e) {
            log.error("导出失败", e);
        }
    }

    @Override
    public Object getHandlers() {
        try {
            // 从 province_leaders 表获取所有负责人
            List<Map<String, Object>> handlers = baseMapper.selectAllHandlers();
            return handlers != null ? handlers : new ArrayList<>();
        } catch (Exception e) {
            log.error("获取负责人列表失败", e);
            return new ArrayList<>();
        }
    }

    @Override
    public void taskHandler(TaskHandlerReq taskHandlerReq) {
        UserContext context = UserContextHolder.getContext();
        TaskListDO taskListDO = this.getById(taskHandlerReq.getId());
        Optional<TaskStatusEnums> byValue = TaskStatusEnums.getByValue(taskHandlerReq.getTaskStatus());
        ValidationUtils.throwIf(byValue.isEmpty(), "不支持的处理状态");
        ValidationUtils.throwIf(!(taskListDO.getTaskStatus().equals(TaskStatusEnums.UN_DO.getValue()) || taskListDO.getTaskStatus().equals(TaskStatusEnums.TRANSFER.getValue())), "");
        taskListDO.setTaskStatus(taskHandlerReq.getTaskStatus());

        String sb = "";
        if (CollectionUtils.isNotEmpty(taskHandlerReq.getFiles())) {
            sb = taskHandlerReq.getFiles().stream().collect(Collectors.joining(","));
        }
        // taskListDO.setHandler(context.getId().toString());
        taskListDO.setHandlePicUrl(sb);
        taskListDO.setHandleDesc(taskHandlerReq.getNotes());
        taskListDO.setCompleteTime(LocalDateTime.now());
        taskListDO.setUpdateTime(LocalDateTime.now());
        this.updateById(taskListDO);
        List<TaskAssignmentDO> tas = taskAssignmentService.list(new LambdaQueryWrapper<TaskAssignmentDO>().eq(TaskAssignmentDO::getTaskId, taskListDO.getId()));
        if (CollectionUtils.isNotEmpty(tas)) {
            tas.stream().filter(t -> t.getHandlerId().equals(context.getId())).forEach(t -> {
                t.setStatus(taskListDO.getTaskStatus());
                if (taskListDO.getTaskStatus().equals(TaskStatusEnums.DONE.getValue())) {
                    t.setCompleteTime(LocalDateTime.now());
                }
                t.setUpdateTime(LocalDateTime.now());
            });
            taskAssignmentService.updateBatchById(tas);
        }
        // 如果申请转派
        if (taskHandlerReq.getTaskStatus().equals(TaskStatusEnums.TRANSFER.getValue())) {
            TransferApplyReq params = new TransferApplyReq();
            params.setTaskId(taskListDO.getId());
            params.setTransferReason(taskHandlerReq.getNotes());
            params.setAttachmentUrls(taskListDO.getHandlePicUrl());
            taskTransferService.applyTransfer(params);
        }
    }

    @Override
    public MyTaskStatisticsRes statistics() {
        Long userId = UserContextHolder.getUserId();
        Integer todo = this.baseMapper.countTask("todo", userId);
        Integer done = this.baseMapper.countTask("done", userId);
        Integer today = this.baseMapper.countTask("today", userId);
        Integer month = this.baseMapper.countTask("month", userId);
        return MyTaskStatisticsRes.builder()
                .waitProcess(Optional.of(todo).orElse(0))
                .theSameMonth(Optional.of(month).orElse(0))
                .todayProcessed(Optional.of(today).orElse(0))
                .accumulatedProcessed(Optional.of(done).orElse(0)).build();
    }


    @Override
    public Object getFilterOptions(String categoryName, String spec, String provinceName, String startDate, String endDate) {
        // 构建查询条件
        LambdaQueryWrapper<TaskListDO> wrapper = new LambdaQueryWrapper<TaskListDO>()
                .eq(TaskListDO::getTaskStatus, TaskStatusEnums.UN_DO.getValue())
                .or()
                .eq(TaskListDO::getTaskStatus, TaskStatusEnums.TRANSFER.getValue());

        if (StrUtil.isNotBlank(categoryName)) {
            wrapper.eq(TaskListDO::getCategoryName, categoryName);
        }
        if (StrUtil.isNotBlank(spec)) {
            wrapper.eq(TaskListDO::getSpec, spec);
        }
        if (StrUtil.isNotBlank(provinceName)) {
            wrapper.eq(TaskListDO::getProvinceName, provinceName);
        }
        if (StrUtil.isNotBlank(startDate)) {
            wrapper.ge(TaskListDO::getCreateTime, startDate + " 00:00:00");
        }
        if (StrUtil.isNotBlank(endDate)) {
            wrapper.le(TaskListDO::getCreateTime, endDate + " 23:59:59");
        }

        List<TaskListDO> filteredTasks = this.list(wrapper);

        // 从筛选后的数据中获取选项
        List<String> categories = filteredTasks.stream()
                .map(TaskListDO::getCategoryName)
                .filter(StrUtil::isNotBlank)
                .distinct()
                .sorted()
                .collect(Collectors.toList());

        List<String> specs = filteredTasks.stream()
                .map(TaskListDO::getSpec)
                .filter(StrUtil::isNotBlank)
                .distinct()
                .sorted()
                .collect(Collectors.toList());

        List<String> provinces = filteredTasks.stream()
                .map(TaskListDO::getProvinceName)
                .filter(StrUtil::isNotBlank)
                .distinct()
                .sorted()
                .collect(Collectors.toList());

        Map<String, Object> result = new HashMap<>();
        result.put("categories", categories);
        result.put("specs", specs);
        result.put("provinces", provinces);

        return result;
    }

    @Override
    public Object getHandlersByProvince(String provinceName) {
        if (StrUtil.isBlank(provinceName)) {
            return new ArrayList<>();
        }

        try {
            // 从 province_leaders 表获取负责人的用户ID和名称
            List<Map<String, Object>> handlers = baseMapper.selectHandlerDetailsByProvince(provinceName);
            return handlers != null ? handlers : new ArrayList<>();
        } catch (Exception e) {
            log.error("获取省区负责人失败", e);
        }

        return new ArrayList<>();
    }

    /**
     * 获取任务总数统计
     */
    private Map<String, Long> getTaskCounts(String provinceName, String handler, String startDate, String endDate) {
        return baseMapper.selectTaskCounts(provinceName, handler, startDate, endDate);
    }

    /**
     * 获取省区排行榜
     */
    private List<Map<String, Object>> getProvinceRanking(String provinceName, String handler, String startDate, String endDate) {
        return baseMapper.selectProvinceStats(provinceName, handler, startDate, endDate);
    }

    /**
     * 获取负责人排行榜
     */
    private List<Map<String, Object>> getHandlerRanking(String provinceName, String handler, String startDate, String endDate) {
        return baseMapper.selectHandlerStats(provinceName, handler, startDate, endDate);
    }

    /**
     * 获取产品维度统计
     */
    private List<Map<String, Object>> getCategoryRanking(String provinceName, String handler, String startDate, String endDate) {
        return baseMapper.selectCategoryStats(provinceName, handler, startDate, endDate);
    }

    /**
     * 获取字符串值，处理null和空值
     */
    private String getStringValue(String value) {
        return StrUtil.isBlank(value) ? null : value.trim();
    }

    /**
     * 批量插入任务（按500条拆分）
     */
    private void batchInsertTasks(List<TaskListDO> taskList) {
        int batchSize = 500;
        for (int i = 0; i < taskList.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, taskList.size());
            List<TaskListDO> batch = taskList.subList(i, endIndex);
            baseMapper.batchInsert(batch);
        }
    }

    @Override
    public boolean pushWxMsg(TaskListDO task) {
        List<TaskAssignmentHandlerResp> handlers = taskAssignmentService.selectHandlers(Collections.singletonList(task.getId()));
        if (CollectionUtils.isEmpty(handlers)) {
            log.error("当前任务未查询到负责人！");
            return false;
        }
        for (TaskAssignmentHandlerResp handler : handlers) {
            if (StringUtils.isEmpty(handler.getOpenId())) {
                log.error("用户:{} openId为空，用户未登录过小程序", handler.getPhone());
                continue;
            }
            WxSubscribeMessageReq params = new WxSubscribeMessageReq();
            params.setOpenId(handler.getOpenId());
            params.setPageUrl(wxMaProperties.getConfigs().get(0).getPageUrl());
            params.setTemplateId(wxMaProperties.getConfigs().get(0).getTemplateId());
            Map<String, String> content = new HashMap<>();
            // 这里修改成统计当前用户总共多少条待处理任务
            content.put("thing4", DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd") + "待办");
            content.put("thing28", task.getPlatformName());
            content.put("thing46", getMaxLenStr(task.getShopName(), 15));
            content.put("thing1", getMaxLenStr(task.getCategoryName() + " " + (StringUtils.isNotEmpty(task.getSpec()) ? task.getSpec() : "未知规格"), 15));
            content.put("thing3", getMaxLenStr(task.getPublishCompany(), 15));
            params.setMsgContent(content);
            notifyService.wxSubscribeMessage(params);
        }
        TaskListDO update = new TaskListDO();
        update.setId(task.getId());
        update.setWxMsgNotice(TaskStatusEnums.DONE.getValue());
        update.setUpdateTime(LocalDateTime.now());
        this.updateById(update);
        return true;
    }

    private String getMaxLenStr(String source, int len) {
        if (StringUtils.isEmpty(source)) {
            return "";
        }
        return source.length() > len ? source.substring(0, len) : source;
    }

    /**
     * 给有任务分配的负责人推送微信消息
     */
    public void pushWxMsgToAssignedHandlers() {
        log.info("开始给有任务分配的负责人推送微信消息");
        try {
            // 统计每个负责人的待处理任务数量（只统计有任务分配的负责人）
            List<TaskAssignmentDO> taskAssignmentDOList = taskAssignmentService.list(
                    new LambdaQueryWrapper<TaskAssignmentDO>()
                            .eq(TaskAssignmentDO::getStatus, TaskStatusEnums.UN_DO.getValue())
            );
            if (CollectionUtils.isEmpty(taskAssignmentDOList)) {
                return;
            }
            Map<Long, Long> handlerTaskCount = taskAssignmentDOList.stream()
                    .collect(Collectors.groupingBy(
                            TaskAssignmentDO::getHandlerId,
                            Collectors.counting()
                    ));

            log.info("统计到的负责人任务数量：{}", handlerTaskCount);

            Set<Long> handlerIds = new HashSet<>();
            // 为每个有任务的负责人推送消息
            for (Map.Entry<Long, Long> entry : handlerTaskCount.entrySet()) {
                Long handlerId = entry.getKey();
                Long taskCount = entry.getValue();

                if (taskCount > 0) {
                    pushWxMsgToHandler(handlerId, taskCount);
                    handlerIds.add(handlerId);
                }
            }

            setMxNotifyStatus(taskAssignmentDOList.stream().filter(t -> handlerIds.contains(t.getHandlerId()))
                    .map(TaskAssignmentDO::getTaskId).toList());
        } catch (Exception e) {
            log.error("推送微信消息失败", e);
        }
    }

    /**
     * 给各省区负责人推送微信消息
     */
    @Override
    public void pushWxMsgToProvinceHandlers() {
        log.info("开始给各省区负责人推送微信消息");
        try {
            List<TaskAssignmentDO> taskAssignmentDOList = taskAssignmentService.list(
                    new LambdaQueryWrapper<TaskAssignmentDO>()
                            .eq(TaskAssignmentDO::getStatus, TaskStatusEnums.UN_DO.getValue())
            );
            if (CollectionUtils.isEmpty(taskAssignmentDOList)) {
                return;
            }
            // 统计每个负责人的待处理任务数量（只统计有任务分配的负责人）
            Map<Long, Long> handlerTaskCount = taskAssignmentDOList.stream()
                    .collect(Collectors.groupingBy(
                            TaskAssignmentDO::getHandlerId,
                            Collectors.counting()
                    ));

            log.info("统计到的负责人任务数量：{}", handlerTaskCount);

            Set<Long> handlerIds = new HashSet<>();
            // 为每个有任务的负责人推送消息
            for (Map.Entry<Long, Long> entry : handlerTaskCount.entrySet()) {
                Long handlerId = entry.getKey();
                Long taskCount = entry.getValue();

                if (taskCount > 0) {
                    pushWxMsgToHandler(handlerId, taskCount);
                    handlerIds.add(handlerId);
                }
            }

            setMxNotifyStatus(taskAssignmentDOList.stream().filter(t -> handlerIds.contains(t.getHandlerId()))
                    .map(TaskAssignmentDO::getTaskId).toList());
        } catch (Exception e) {
            log.error("推送微信消息失败", e);
        }
    }

    private void setMxNotifyStatus(Collection<Long> taskIds) {
        if (CollectionUtils.isEmpty(taskIds)) {
            return;
        }
        // 更新任务的微信通知状态
        TaskListDO taskListDO = new TaskListDO();
        taskListDO.setWxMsgNotice(TaskStatusEnums.DONE.getValue());
        taskListDO.setUpdateTime(LocalDateTime.now());
        this.update(taskListDO, new LambdaQueryWrapper<TaskListDO>().in(TaskListDO::getId, taskIds));
    }

    /**
     * 给指定省区负责人推送微信消息
     */
    private void pushWxMsgToProvinceHandlers(String provinceName, Long taskCount) {
        try {
            // 获取省区负责人id
            List<String> handlers = baseMapper.selectHandlerIdsByProvince(provinceName);
            if (CollectionUtils.isEmpty(handlers)) {
                log.warn("消息推送：省区 {} 未找到负责人", provinceName);
                return;
            }

            for (String handlerUserId : handlers) {
                // 获取用户信息
                UserDO user = userService.getById(handlerUserId);
                if (user == null || StrUtil.isBlank(user.getOpenId())) {
                    log.warn("消息推送：用户 {} openId为空，用户未登录过小程序", handlerUserId);
                    continue;
                }

                WxSubscribeMessageReq params = new WxSubscribeMessageReq();
                params.setOpenId(user.getOpenId());
                params.setPageUrl(wxMaProperties.getConfigs().get(0).getPageUrl());
                params.setTemplateId(wxMaProperties.getConfigs().get(0).getTemplateId());

                Map<String, String> content = new HashMap<>();
                content.put("thing4", DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd") + "待办");
                content.put("thing28", "任务通知");
                content.put("thing46", provinceName);
                content.put("thing1", "待处理任务数量：" + taskCount + "条");
                content.put("thing3", "请及时处理");
                params.setMsgContent(content);

                notifyService.wxSubscribeMessage(params);

                // 推送成功后，订阅次数-1
                if (user.getSubscribeCount() != null && user.getSubscribeCount() > 0) {
                    UserDO updateUser = new UserDO();
                    updateUser.setId(user.getId());
                    updateUser.setSubscribeCount(user.getSubscribeCount() - 1);
                    updateUser.setUpdateTime(LocalDateTime.now());
                    userService.updateById(updateUser);
                }

                log.info("消息推送：已给用户 {} 推送微信消息，省区：{}，任务数：{}", handlerUserId, provinceName, taskCount);
            }
        } catch (Exception e) {
            log.error("消息推送：给省区 {} 负责人推送消息失败", provinceName, e);
        }
    }

    /**
     * 给指定负责人推送微信消息
     */
    private void pushWxMsgToHandler(Long handlerId, Long taskCount) {
        try {
            // 获取用户信息
            UserDO user = userService.getById(handlerId);
            if (user == null || StrUtil.isBlank(user.getOpenId())) {
                log.warn("消息推送失败：用户 {} 用户未登录过小程序！", handlerId);
                return;
            }
            if (null == user.getSubscribeCount() || user.getSubscribeCount() < 1) {
                log.warn("消息推送失败：用户 {} 用户订阅微信消息数量不足，当前数量：{}", handlerId, user.getSubscribeCount());
                return;
            }
            WxSubscribeMessageReq params = new WxSubscribeMessageReq();
            params.setOpenId(user.getOpenId());
            params.setPageUrl(wxMaProperties.getConfigs().get(0).getPageUrl());
            params.setTemplateId(wxMaProperties.getConfigs().get(0).getTemplateId());

            Map<String, String> content = new HashMap<>();
            content.put("thing4", DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd") + "待办");
            content.put("thing28", "任务通知");
            content.put("thing46", user.getNickname() != null ? user.getNickname() : user.getUsername());
            content.put("thing1", "待处理任务数量：" + taskCount + "条");
            content.put("thing3", "请及时处理");
            params.setMsgContent(content);

            notifyService.wxSubscribeMessage(params);

            log.info("消息推送：已给用户 {} 推送微信消息，任务数：{}", handlerId, taskCount);

        } catch (Exception e) {
            log.error("消息推送：给用户 {} 推送消息失败", handlerId, e);
        }
    }

    /**
     * 根据负责人信息创建任务分配
     */
    private void createTaskAssignments(TaskListDO task, String batchId) {
        String provinceName = task.getProvinceName();
        String assignedHandler = task.getHandler(); // Excel中的负责人
        if (StringUtils.isEmpty(provinceName)) {
            return;
        }
        try {
            // 获取省区所有负责人
            List<Map<String, Object>> allHandlers = baseMapper.selectHandlerDetailsByProvince(provinceName);
            if (CollectionUtils.isEmpty(allHandlers)) {
                log.warn("省区 {} 未找到负责人", provinceName);
                return;
            }

            List<TaskAssignmentDO> assignments = new ArrayList<>();

            if (StrUtil.isBlank(assignedHandler)) {
                // 负责人为空，所有负责人都生成分配
                for (Map<String, Object> handler : allHandlers) {
                    TaskAssignmentDO assignment = createAssignment(task.getId().toString(), handler, batchId);
                    assignments.add(assignment);
                }
            } else {
                // 匹配指定负责人
                Map<String, Object> matchedHandler = allHandlers.stream()
                        .filter(h -> assignedHandler.equals(h.get("name")))
                        .findFirst()
                        .orElse(null);

                if (matchedHandler != null) {
                    TaskAssignmentDO assignment = createAssignment(task.getId().toString(), matchedHandler, batchId);
                    assignments.add(assignment);
                } else {
                    log.warn("任务 {} 的负责人 {} 在省区 {} 中未找到", task.getId(), assignedHandler, provinceName);
                }
            }

            if (!assignments.isEmpty()) {
                taskAssignmentService.saveBatch(assignments);
            }

        } catch (Exception e) {
            log.error("创建任务分配失败，任务ID: {}", task.getId(), e);
        }
    }

    /**
     * 创建单个任务分配记录
     */
    private TaskAssignmentDO createAssignment(String taskId, Map<String, Object> handler, String batchId) {
        TaskAssignmentDO assignment = new TaskAssignmentDO();
        assignment.setTaskId(Long.valueOf(taskId));
        assignment.setHandlerId(Long.valueOf(handler.get("id").toString()));
        assignment.setHandlerName(handler.get("name").toString());
        assignment.setBatchId(batchId);
        assignment.setStatus(TaskStatusEnums.UN_DO.getValue());
        assignment.setCreateTime(LocalDateTime.now());
        assignment.setUpdateTime(LocalDateTime.now());
        return assignment;
    }

    @Override
    public Object getTaskAssignments(String taskId) {
        List<TaskAssignmentDO> assignments = taskAssignmentService.list(
                new LambdaQueryWrapper<TaskAssignmentDO>()
                        .eq(TaskAssignmentDO::getTaskId, taskId)
        );

        // 为每个分配记录添加省区名称
        return assignments.stream().map(assignment -> {
            Map<String, Object> result = new HashMap<>();
            result.put("id", assignment.getId());
            result.put("taskId", assignment.getTaskId());
            result.put("handlerName", assignment.getHandlerName());
            result.put("createTime", assignment.getCreateTime());
            result.put("status", assignment.getStatus());

            // 获取任务信息以获取省区名称
            TaskListDO task = this.getById(assignment.getTaskId());
            if (task != null) {
                result.put("provinceName", task.getProvinceName());
                result.put("provinceCode", task.getProvinceCode());
            }

            return result;
        }).collect(Collectors.toList());
    }

    @Override
    public void updateTaskAssignment(String assignmentId, Object data) {
        TaskAssignmentDO assignment = taskAssignmentService.getById(assignmentId);
        if (assignment != null) {
            @SuppressWarnings("unchecked")
            Map<String, Object> updateData = (Map<String, Object>) data;

            // 更新处理人信息
            if (updateData.containsKey("handlerId")) {
                assignment.setHandlerId(Long.valueOf(updateData.get("handlerId").toString()));
            }
            if (updateData.containsKey("handler")) {
                assignment.setHandlerName(updateData.get("handler").toString());
            }

            assignment.setUpdateTime(LocalDateTime.now());
            taskAssignmentService.updateById(assignment);
        }
    }

    @Override
    public Object getBatches() {
        return importBatchService.getBatchOptions();
    }

    @Override
    public Object getSearchOptions() {
        try {
            List<TaskListDO> allTasks = this.list();

            Map<String, Object> result = new HashMap<>();
            result.put("platforms", allTasks.stream().map(TaskListDO::getPlatformName).filter(StrUtil::isNotBlank).distinct().sorted().map(item -> Map.of("label", item, "value", item)).collect(Collectors.toList()));
            result.put("provinces", allTasks.stream().map(TaskListDO::getProvinceName).filter(StrUtil::isNotBlank).distinct().sorted().map(item -> Map.of("label", item, "value", item)).collect(Collectors.toList()));
            result.put("shops", allTasks.stream().map(TaskListDO::getShopName).filter(StrUtil::isNotBlank).distinct().sorted().map(item -> Map.of("label", item, "value", item)).collect(Collectors.toList()));
            result.put("companies", allTasks.stream().map(TaskListDO::getPublishCompany).filter(StrUtil::isNotBlank).distinct().sorted().map(item -> Map.of("label", item, "value", item)).collect(Collectors.toList()));
            result.put("categories", allTasks.stream().map(TaskListDO::getCategoryName).filter(StrUtil::isNotBlank).distinct().sorted().map(item -> Map.of("label", item, "value", item)).collect(Collectors.toList()));
            result.put("specs", allTasks.stream().map(TaskListDO::getSpec).filter(StrUtil::isNotBlank).distinct().sorted().map(item -> Map.of("label", item, "value", item)).collect(Collectors.toList()));

            return result;
        } catch (Exception e) {
            log.error("获取搜索选项失败", e);
            return new HashMap<>();
        }
    }

    @Override
    public void batchUpdateHandler(List<String> taskIds, Long newHandlerId, String newHandlerName) {
        if (CollectionUtils.isEmpty(taskIds) || newHandlerId == null || StrUtil.isBlank(newHandlerName)) {
            return;
        }

        // 更新 task_assignment 表中的负责人信息
        List<TaskAssignmentDO> assignments = taskAssignmentService.list(
                new LambdaQueryWrapper<TaskAssignmentDO>()
                        .in(TaskAssignmentDO::getTaskId, taskIds)
        );

        if (CollectionUtils.isNotEmpty(assignments)) {
            assignments.forEach(assignment -> {
                assignment.setHandlerId(newHandlerId);
                assignment.setHandlerName(newHandlerName);
                assignment.setUpdateTime(LocalDateTime.now());
            });
            taskAssignmentService.updateBatchById(assignments);
        }

        log.info("批量更新负责人成功，任务数量: {}, 新负责人: {}", taskIds.size(), newHandlerName);
    }

    @Override
    public boolean deleteTaskWithAssignments(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return false;
        }

        // 先删除任务分配记录
        taskAssignmentService.remove(
                new LambdaQueryWrapper<TaskAssignmentDO>()
                        .in(TaskAssignmentDO::getTaskId, ids)
        );

        // 再删除任务记录
        return this.removeByIds(ids);
    }

    @Override
    public void handleTransfer(TransferHandleReq req) {
        taskTransferService.handleTransfer(req);
    }

    @Override
    public Object getTransferChain(Long taskId) {
        return taskTransferService.getTransferChain(taskId);
    }

    /**
     * 批量创建任务分配记录（公开方法供优化服务调用）
     */
    public List<Long> batchCreateTaskAssignments(List<TaskListDO> taskList, String batchId) {
        List<TaskAssignmentDO> allAssignments = new ArrayList<>();
        List<Long> failIds = new ArrayList<>();

        // 批量查询转派记录
        Map<String, Long> transferReceivers = batchQueryTransferReceivers(taskList);
        if(MapUtils.isEmpty(transferReceivers)){
            return failIds;
        }
        List<UserDO> allTransferUsers = userService.list(new LambdaQueryWrapper<UserDO>().in(UserDO::getId, transferReceivers.values()));

        for (TaskListDO task : taskList) {
            if (task.getMatchHandler() == 1 && StrUtil.isNotBlank(task.getProvinceName())) {
                // 检查是否有转派记录
                String shopCategorySpecKey = task.getShopName() + "_" + task.getCategoryName() + "_" + (task.getSpec() != null ? task.getSpec() : "");
                Long transferReceiverId = transferReceivers.get(shopCategorySpecKey);

                List<TaskAssignmentDO> assignments;
                if (transferReceiverId != null) {
                    // 有转派记录，直接分配给转派接收人
                    assignments = createTransferAssignment(task.getId(), transferReceiverId, batchId, allTransferUsers);
                    log.info("匹配到转配记录：店铺+药品+规格：{}，原负责人：{}，转派到：{}",shopCategorySpecKey,task.getHandler(),transferReceiverId);
                } else {
                    // 无转派记录，按原逻辑分配
                    assignments = ((TaskAssignmentServiceImpl) taskAssignmentService)
                            .createAssignmentList(task.getId(), task.getProvinceName(), batchId, task.getHandler());
                }

                if (CollectionUtils.isEmpty(assignments)) {
                    failIds.add(task.getId());
                    continue;
                }
                allAssignments.addAll(assignments);
            } else {
                failIds.add(task.getId());
            }
        }

        if (!allAssignments.isEmpty()) {
            taskAssignmentService.batchSaveAssignments(allAssignments);
        }
        return failIds;
    }

    /**
     * 批量查询转派接收人
     */
    private Map<String, Long> batchQueryTransferReceivers(List<TaskListDO> taskList) {
        // 收集所有店铺+品种+规格组合
        Set<String> shopCategorySpecKeys = taskList.stream()
                .filter(task -> StrUtil.isNotBlank(task.getShopName()) && StrUtil.isNotBlank(task.getCategoryName()))
                .map(task -> task.getShopName() + "_" + task.getCategoryName() + "_" + (task.getSpec() != null ? task.getSpec() : ""))
                .collect(Collectors.toSet());

        if (shopCategorySpecKeys.isEmpty()) {
            return new HashMap<>();
        }

        // 批量查询转派记录
        List<Map<String, Object>> results = baseMapper.selectLatestTransferReceivers(shopCategorySpecKeys);

        // 转换为Map结构
        Map<String, Long> transferReceivers = new HashMap<>();
        for (Map<String, Object> result : results) {
            String key = (String) result.get("shopCategorySpecKey");
            Long toUserId = Long.valueOf(result.get("toUserId").toString());
            transferReceivers.put(key, toUserId);
        }

        return transferReceivers;
    }

    /**
     * 创建转派分配记录
     */
    private List<TaskAssignmentDO> createTransferAssignment(Long taskId, Long receiverId, String batchId, List<UserDO> allTransferUsers) {
        try {
            Optional<UserDO> first = allTransferUsers.stream().filter(u -> u.getId().equals(receiverId)).findFirst();
            if (first.isEmpty()) {
                log.warn("转派接收人不存在: {}", receiverId);
                return new ArrayList<>();
            }

            // 获取接收人信息
            UserDO receiver = first.get();

            TaskAssignmentDO assignment = new TaskAssignmentDO();
            assignment.setTaskId(taskId);
            assignment.setHandlerId(receiverId);
            assignment.setHandlerName(receiver.getNickname() != null ? receiver.getNickname() : receiver.getUsername());
            assignment.setStatus("un_do");
            assignment.setBatchId(batchId);
            assignment.setCreateTime(LocalDateTime.now());
            assignment.setUpdateTime(LocalDateTime.now());

            return Collections.singletonList(assignment);
        } catch (Exception e) {
            log.error("创建转派分配记录失败: taskId={}, receiverId={}", taskId, receiverId, e);
            return new ArrayList<>();
        }
    }

    @Override
    public void pushSmsMsgToProvinceHandlers() throws Exception {
        // 统计所有未处理的任务，逻辑与pushWxMsgToProvinceHandlers一致
        List<TaskAssignmentDO> taskAssignmentDOList = taskAssignmentService.list(
                new LambdaQueryWrapper<TaskAssignmentDO>()
                        .eq(TaskAssignmentDO::getStatus, TaskStatusEnums.UN_DO.getValue())
        );

        if (CollectionUtils.isEmpty(taskAssignmentDOList)) {
            log.info("没有未处理的任务，跳过短信发送");
            return;
        }

        // 统计每个负责人的待处理任务数量
        Map<Long, Long> handlerTaskCount = taskAssignmentDOList.stream()
                .collect(Collectors.groupingBy(
                        TaskAssignmentDO::getHandlerId,
                        Collectors.counting()
                ));

        log.info("统计到的负责人任务数量：{}", handlerTaskCount);

        // 获取所有负责人的手机号
        List<Long> handlerIds = new ArrayList<>(handlerTaskCount.keySet());
        List<UserDO> users = userService.listByIds(handlerIds);

        // 按任务数量分组发送短信
        Map<Integer, List<String>> phonesByTaskCount = new HashMap<>();

        for (UserDO user : users) {
            Long taskCount = handlerTaskCount.get(user.getId());
            if (taskCount != null && taskCount > 0 && user.getPhone() != null) {
                int count = taskCount.intValue();
                phonesByTaskCount.computeIfAbsent(count, k -> new ArrayList<>()).add(user.getPhone());
            }
        }

        // 发送短信
        for (Map.Entry<Integer, List<String>> entry : phonesByTaskCount.entrySet()) {
            Integer taskNum = entry.getKey();
            List<String> phones = entry.getValue();

            if (!phones.isEmpty()) {
                String[] phoneArray = phones.toArray(new String[0]);
                log.info("向手机：{}发送短信通知！", phones.get(0));
                // smsService.sendNotice(phoneArray, taskNum);
                //log.info("已向{}个负责人发送短信，任务数量：{}", phones.size(), taskNum);
            }
        }

        //smsService.sendNotice(new String[]{"15908891152"}, 1);
        // 更新短信通知状态
        TaskListDO update = new TaskListDO();
        update.setSmsNotice(NotifyStatusEnums.DONE.getColor());
        update.setUpdateTime(LocalDateTime.now());
        this.update(update, new LambdaQueryWrapper<TaskListDO>().in(TaskListDO::getId, taskAssignmentDOList.stream().map(TaskAssignmentDO::getTaskId).collect(Collectors.toList())));
    }

    @Override
    public String asyncExport(TaskListCustomQuery query) {
        Long userId = UserContextHolder.getUserId();
        String taskId = exportTaskService.createTask("任务管理导出", userId);
        performAsyncExport(query, taskId);
        return taskId;
    }

    @Async
    public void performAsyncExport(TaskListCustomQuery query, String taskId) {
        try {
            exportTaskService.updateTaskStatus(taskId, "PROCESSING", 10, null);
            
            String fileName = "任务管理导出_" + DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + ".xlsx";
            String filePath = System.getProperty("java.io.tmpdir") + File.separator + fileName;
            
            exportTaskService.updateTaskStatus(taskId, "PROCESSING", 30, null);
            
            List<MyTaskRes> exportList = this.baseMapper.myPageQuery(new Page<>(1, Integer.MAX_VALUE), query).getRecords();
            List<MyTaskExportRes> list = new ArrayList<>();
            
            if (CollectionUtils.isNotEmpty(exportList)) {
                exportTaskService.updateTaskStatus(taskId, "PROCESSING", 50, null);
                
                List<TaskAssignmentHandlerResp> assignmentHandlers = taskAssignmentService.selectHandlers(exportList.stream().map(MyTaskRes::getId).toList());
                Map<Long, List<Map<String, Object>>> transferChainMap = baseMapper.selectTransferChainForExport(exportList.stream().map(MyTaskRes::getId).collect(Collectors.toSet()))
                        .stream().collect(Collectors.groupingBy(t -> Long.valueOf(t.get("taskId").toString())));
                
                exportTaskService.updateTaskStatus(taskId, "PROCESSING", 70, null);
                
                for (MyTaskRes myTaskRes : exportList) {
                    MyTaskExportRes exportRes = new MyTaskExportRes();
                    BeanUtils.copyProperties(myTaskRes, exportRes);
                    exportRes.setTaskStatus(TaskStatusEnums.getByValue(myTaskRes.getTaskStatus()).orElse(TaskStatusEnums.UN_DO).getDescription());
                    exportRes.setWxMsgNotice(NotifyStatusEnums.getByValue(myTaskRes.getWxMsgNotice()).orElse(NotifyStatusEnums.UN_DO).getDescription());
                    exportRes.setVoiceNotice(NotifyStatusEnums.getByValue(myTaskRes.getVoiceNotice()).orElse(NotifyStatusEnums.UN_DO).getDescription());
                    if (null != myTaskRes.getCompleteTime()) {
                        exportRes.setCompleteTimeStr(DateUtil.format(myTaskRes.getCompleteTime(), "yyyy-MM-dd"));
                    }
                    exportRes.setCreateTimeStr(DateUtil.format(myTaskRes.getCreateTime(), "yyyy-MM-dd"));
                    exportRes.setDirectors(assignmentHandlers.stream().filter(ah -> ah.getTaskId().equals(myTaskRes.getId()))
                            .map(TaskAssignmentHandlerResp::getHandlerName).collect(Collectors.joining()));
                    if (StringUtils.isNotEmpty(myTaskRes.getHandler())) {
                        exportRes.setHandlerName(assignmentHandlers.stream().map(TaskAssignmentHandlerResp::getHandlerName).distinct()
                                .filter(handlerName -> handlerName.equals(myTaskRes.getHandler())).collect(Collectors.joining(",")));
                    }
                    
                    List<Map<String, Object>> taskTransferChain = transferChainMap.get(myTaskRes.getId());
                    if (CollectionUtils.isNotEmpty(taskTransferChain)) {
                        String chainStr = taskTransferChain.stream()
                                .map(t -> {
                                    String s = t.get("fromUserName") + "→";
                                    if (null != t.get("toUserName")) {
                                        s += t.get("toUserName");
                                    }
                                    return s;
                                })
                                .collect(Collectors.joining("→"));
                        exportRes.setTransferChain(chainStr);
                        
                        String attachments = taskTransferChain.stream()
                                .map(t -> (String) t.get("attachmentUrls"))
                                .filter(StrUtil::isNotBlank)
                                .collect(Collectors.joining(";"));
                        exportRes.setTransferAttachments(attachments);
                    } else {
                        exportRes.setTransferChain("");
                        exportRes.setTransferAttachments("");
                    }
                    
                    list.add(exportRes);
                }
            }
            
            exportTaskService.updateTaskStatus(taskId, "PROCESSING", 90, null);
            
            // 先写入本地文件
            try (FileOutputStream fos = new FileOutputStream(filePath)) {
                EasyExcel.write(fos, MyTaskExportRes.class)
                        .sheet("任务管理")
                        .doWrite(list);
            }
            
            // 上传到云存储
            String fileUrl;
            try (FileInputStream fis = new FileInputStream(filePath)) {
                fileUrl = cloudStorageService.upload(fis, fileName);
            }
            
            // 删除本地文件
            new File(filePath).delete();
            
            exportTaskService.completeTask(taskId, fileUrl, fileName);
            
        } catch (Exception e) {
            log.error("异步导出失败", e);
            exportTaskService.updateTaskStatus(taskId, "FAILED", null, e.getMessage());
        }
    }

    @Override
    public List<ExportTaskResp> getExportTasks() {
        Long userId = UserContextHolder.getUserId();
        return exportTaskService.getUserTasks(userId);
    }

    @Override
    public ExportTaskResp getExportTask(String taskId) {
        return exportTaskService.getTaskDetail(taskId);
    }

    @Override
    public void downloadExportFile(String taskId, HttpServletResponse response) {
        try {
            ExportTaskResp task = exportTaskService.getTaskDetail(taskId);
            if (task == null || !"COMPLETED".equals(task.getStatus())) {
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
            
            ExportTaskDO taskEntity = exportTaskService.getOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<ExportTaskDO>().eq(ExportTaskDO::getTaskId, taskId));
            if (taskEntity == null || StrUtil.isBlank(taskEntity.getFilePath())) {
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
            
            // 直接重定向到云存储文件URL
            response.sendRedirect(taskEntity.getFilePath());
        } catch (Exception e) {
            log.error("下载文件失败", e);
        }
    }

    /**
     * Excel导入监听器，分批处理数据
     */
    private class ExcelImportListener implements com.alibaba.excel.read.listener.ReadListener<Map<Integer, String>> {
        private static final int BATCH_SIZE = 500;
        private final String batchId;
        private final List<DictItemDO> provinceDict;
        private final List<TaskListDO> taskList = new ArrayList<>();
        private int successCount = 0;
        private int failCount = 0;

        public ExcelImportListener(String batchId, List<DictItemDO> provinceDict) {
            this.batchId = batchId;
            this.provinceDict = provinceDict;
        }

        @Override
        public void invoke(Map<Integer, String> row, com.alibaba.excel.context.AnalysisContext context) {
            try {
                TaskListDO task = new TaskListDO();

                // 根据Excel列索引设置字段
                task.setPlatformName(getStringValue(row.get(0)));
                String provinceName = getStringValue(row.get(1));
                String assignedHandler = getStringValue(row.get(2));
                task.setShopName(getStringValue(row.get(3)));
                task.setPublishCompany(getStringValue(row.get(4)));
                task.setCategoryName(getStringValue(row.get(5)));
                task.setSpec(getStringValue(row.get(6)));
                task.setCurrentPrice(getStringValue(row.get(7)));
                task.setGoodsLink(getStringValue(row.get(8)));
                task.setMatchHandler(1);

                // 跳过空行
                if (StrUtil.isAllBlank(task.getPlatformName(), provinceName, task.getShopName(),
                        task.getPublishCompany(), task.getCategoryName(), task.getSpec())) {
                    task.setMatchHandler(0);
                    taskList.add(task);
                    failCount++;
                    return;
                }

                task.setHandler(assignedHandler);
                task.setTaskStatus(TaskStatusEnums.UN_DO.getValue());
                task.setWxMsgNotice(TaskStatusEnums.UN_DO.getValue());
                task.setSmsNotice(TaskStatusEnums.UN_DO.getValue());
                task.setVoiceNotice(TaskStatusEnums.UN_DO.getValue());
                task.setBatchId(batchId);

                // 匹配省区编码
                String provinceCode = matchProvinceCode(provinceName, provinceDict);
                if (provinceCode == null) {
                    task.setMatchHandler(0);
                    taskList.add(task);
                    failCount++;
                    return;
                }
                task.setProvinceCode(provinceCode);
                task.setProvinceName(provinceName);

                taskList.add(task);
                successCount++;

                // 达到批次大小时处理数据
                if (taskList.size() >= BATCH_SIZE) {
                    processBatch();
                }
            } catch (Exception e) {
                log.error("处理第{}行数据失败", context.readRowHolder().getRowIndex() + 1, e);
                failCount++;
            }
        }

        @Override
        public void doAfterAllAnalysed(com.alibaba.excel.context.AnalysisContext context) {
            // 读取完成后处理剩余数据
            processRemaining();
        }

        private void processBatch() {
            if (!taskList.isEmpty()) {
                LocalDateTime now = LocalDateTime.now();
                
                // 创建副本避免引用问题
                List<TaskListDO> batchTasks = new ArrayList<>();
                for (TaskListDO task : taskList) {
                    task.setCreateTime(now);
                    task.setUpdateTime(now);
                    task.setId(null); // 确保 ID 为空
                    batchTasks.add(task);
                }
                
                // 批量插入
                baseMapper.batchInsert(batchTasks);
                
                // 创建任务分配
                List<Long> failTaskIds = batchCreateTaskAssignments(batchTasks, batchId);

                if (CollectionUtils.isNotEmpty(failTaskIds)) {
                    TaskListDO failTask = new TaskListDO();
                    failTask.setMatchHandler(0);
                    update(failTask,new LambdaQueryWrapper<TaskListDO>().in(TaskListDO::getId, failTaskIds));
                    failCount += failTaskIds.size();
                }
                taskList.clear(); // 立即清空，避免重复处理
            }
        }

        public void processRemaining() {
            processBatch();
        }

        public int getSuccessCount() {
            return successCount;
        }

        public int getFailCount() {
            return failCount;
        }
    }

}